package com.guigui.gulimall.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guigui.common.exception.NoStockException;
import com.guigui.common.to.MemberDto;
import com.guigui.common.to.OrderTo;
import com.guigui.common.to.ware.WareStockVo;
import com.guigui.common.utils.PageUtils;
import com.guigui.common.utils.Query;
import com.guigui.gulimall.order.constant.OrderConstant;
import com.guigui.gulimall.order.constant.OrderStatusEnum;
import com.guigui.gulimall.order.dao.OrderDao;
import com.guigui.gulimall.order.dto.OrderLockDto;
import com.guigui.gulimall.order.dto.OrderSubmitDto;
import com.guigui.gulimall.order.dto.ProductLockDto;
import com.guigui.gulimall.order.entity.OrderEntity;
import com.guigui.gulimall.order.entity.OrderItemEntity;
import com.guigui.gulimall.order.feign.CartFeignService;
import com.guigui.gulimall.order.feign.MemberAddressService;
import com.guigui.gulimall.order.feign.ProductFeignService;
import com.guigui.gulimall.order.feign.WareFeignService;
import com.guigui.gulimall.order.interceptor.GulimallAuthInterceptor;
import com.guigui.gulimall.order.service.OrderItemService;
import com.guigui.gulimall.order.service.OrderService;
import com.guigui.gulimall.order.to.OrderCreateTo;
import com.guigui.gulimall.order.to.OrderLockTo;
import com.guigui.gulimall.order.to.SkuInfo;
import com.guigui.gulimall.order.to.SpuInfo;
import com.guigui.gulimall.order.vo.*;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Service("orderService")
//@RabbitListener(queues = {"gulimall.order"})
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    public static ThreadLocal<OrderSubmitDto> threadLocal = new ThreadLocal<>();

    @Autowired
    private MemberAddressService memberAddressService;

    @Autowired
    private CartFeignService cartFeignService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ThreadPoolExecutor executor;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private WareFeignService wareFeignService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo getOrderConfirm() {
        MemberDto user = GulimallAuthInterceptor.memberThreadLocal.get();
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        CompletableFuture<Void> getCartItemsFuture = CompletableFuture.runAsync(() -> {
            try {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                // 获取购物车内商品
                List<CartItem> cartItems = cartFeignService.getCartItems();
                cartItems = cartItems.stream().filter(CartItem::isCheck).collect(Collectors.toList());
                orderConfirmVo.setCartItems(cartItems);
            } finally {
                RequestContextHolder.resetRequestAttributes();
            }
        }, executor);


        // 有无库存映射
        CompletableFuture<Void> queryStock = getCartItemsFuture.thenRunAsync(() -> {
            List<CartItem> cartItems = orderConfirmVo.getCartItems();
            if (cartItems != null && cartItems.size() > 0) {
                List<Long> collect = cartItems.stream().map(CartItem::getSkuId).collect(Collectors.toList());
                List<WareStockVo> wareStockVos = wareFeignService.hasStock(collect);
                Map<Long, Boolean> stockMap = wareStockVos.stream().collect(Collectors.toMap(WareStockVo::getSkuId, WareStockVo::getStock));
                cartItems.forEach(item -> item.setHasStock(stockMap.get(item.getSkuId())));


                // 没库存，抛异常
//                long count = wareStockVos.stream().filter(WareStockVo::getStock).count();
//                if (count != wareStockVos.size()) {
//                    throw new NoStockException();
//                }
            }
        }, executor);
//                .whenComplete((res, e) -> {
//            System.out.println("没库存了");
//        });


        CompletableFuture<Void> getAddressFuture = CompletableFuture.runAsync(() -> {
            // 获取地址
            List<OrderAddressVo> memberAddress = memberAddressService.getMemberAddress(user.getId());
            orderConfirmVo.setAddress(memberAddress);
        }, executor);


        orderConfirmVo.setIntegration(1000);
        String token = UUID.randomUUID().toString();

        orderConfirmVo.setToken(token);
        // redis放入token
        MemberDto member = GulimallAuthInterceptor.memberThreadLocal.get();
        String tokenKey = OrderConstant.ORDER_TOKEN_PREFIX + member.getId();
        redisTemplate.opsForValue().set(tokenKey, token, 10, TimeUnit.MINUTES);

        try {
            CompletableFuture.allOf(getCartItemsFuture, getAddressFuture, queryStock).get();
        } catch (InterruptedException | ExecutionException | NoStockException e) {
//            e.printStackTrace();
            if (e instanceof ExecutionException) {
                Throwable cause = e.getCause();
                if (cause.getClass() == NoStockException.class)
                throw new NoStockException();
            }
        }

        return orderConfirmVo;
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public OrderSubmitSuccessVo submitOrder(OrderSubmitDto submitDto) throws NoStockException{
        OrderSubmitSuccessVo orderSubmitSuccessVo = new OrderSubmitSuccessVo();
        // 校验令牌
        MemberDto member = GulimallAuthInterceptor.memberThreadLocal.get();
        String tokenKey = OrderConstant.ORDER_TOKEN_PREFIX + member.getId();

        // 1匹配成功 0匹配失败。比对并删除token
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        Long execute = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(tokenKey), submitDto.getToken());

        // token校验成功后处理
        if (execute != null && execute == 1) {
            threadLocal.set(submitDto);
            // 返回code
            orderSubmitSuccessVo.setCode(1);

            // 创建订单
            OrderCreateTo order = createOrder();
            orderSubmitSuccessVo.setOrder(order);

            // 锁定库存
            List<WareStockVo> wareStockVos = orderLock(order.getOrder(), order.getOrderItems());
            if (wareStockVos == null) {
                throw new NoStockException();
            }

            // 保存订单数据
            saveOrder(orderSubmitSuccessVo.getOrder());
            // 延时队列发送消息，携带订单
            rabbitTemplate.convertAndSend("order-event-exchange", "order.create.order", order.getOrder());

//            long count = wareStockVos.stream().filter(WareStockVo::getStock).count();
//            if (wareStockVos.size() != count) {
//                throw new NoStockException();
//            }

        }
        return orderSubmitSuccessVo;
    }

    public OrderCreateTo createOrder() {
        OrderCreateTo orderCreateTo = new OrderCreateTo();

        OrderEntity orderEntity = buildOrder();
        orderCreateTo.setOrder(orderEntity);

        // 购物项设置
        List<CartItem> cartSelectItems = cartFeignService.getCartItems();
        List<OrderItemEntity> orderItems = cartSelectItems.stream().filter(CartItem::isCheck).map(item -> buildOrderItem(item, orderEntity.getOrderSn())).collect(Collectors.toList());
        orderCreateTo.setOrderItems(orderItems);

        // 验证价格
        computePrice(orderEntity, orderItems);

        return orderCreateTo;
    }

    private List<WareStockVo> orderLock(OrderEntity orderEntity, List<OrderItemEntity> orderItems) {
        OrderLockTo orderLock = new OrderLockTo();
        List<CartItem> collect = orderItems.stream().map(item -> {
            CartItem cartItem = new CartItem();
            cartItem.setSkuId(item.getSkuId());
            cartItem.setCount(item.getSkuQuantity());
            cartItem.setTitle(item.getSkuName());
            return cartItem;
        }).collect(Collectors.toList());

        orderLock.setCartProduct(collect);
        orderLock.setOrderSn(orderEntity.getOrderSn());

        List<ProductLockDto> productLockDtos = collect.stream().map(c -> {
            ProductLockDto productLockDto = new ProductLockDto();
            productLockDto.setCount(c.getCount());
            productLockDto.setTitle(c.getTitle());
            productLockDto.setSkuId(c.getSkuId());
            return productLockDto;
        }).collect(Collectors.toList());

        OrderLockDto orderLockDto = new OrderLockDto();
        orderLockDto.setProductsLock(productLockDtos);
        orderLockDto.setOrderSn(orderLock.getOrderSn());


        return wareFeignService.lockProduct(orderLockDto);
    }

    /**
     * 保存订单
     * @param orderCreateTo
     */
    private boolean saveOrder(OrderCreateTo orderCreateTo) {
        List<OrderItemEntity> orderItems = orderCreateTo.getOrderItems();
        OrderEntity order = orderCreateTo.getOrder();
        order.setModifyTime(new Date());
        return orderItemService.saveBatch(orderItems) && this.save(order);
    }

    /**
     * 未付款就关闭订单
     */
    @Override
    public void closeOrder(OrderEntity order) {
        OrderEntity orderEntity = this.getById(order.getId());
        // 未付款 关订单
        if (orderEntity != null && orderEntity.getStatus().equals(OrderStatusEnum.CREATE_NEW.getCode())) {
            // 订单状态置为取消
            OrderEntity entity = new OrderEntity();
            entity.setId(orderEntity.getId());
            entity.setStatus(OrderStatusEnum.CANCELED.getCode());
            System.out.println("订单超时关闭订单");
            this.updateById(entity);

            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(this.getById(order.getId()), orderTo);

            // ware服务接受这个消息，准备回滚库存
            rabbitTemplate.convertAndSend("order-event-exchange", "order.release.other", orderTo);
        }

    }

    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItems) {
        // fix freight set 8
        orderEntity.setFreightAmount(new BigDecimal(8));

        BigDecimal coupon = new BigDecimal(0);
        BigDecimal integration = new BigDecimal(0);
        BigDecimal promotion = new BigDecimal(0);
        BigDecimal giftGrowth = new BigDecimal(0);
        BigDecimal giftIntegration = new BigDecimal(0);

        BigDecimal orderTotalPrice = new BigDecimal(0);

        for (OrderItemEntity orderItem : orderItems) {
            coupon = coupon.add(orderItem.getCouponAmount());
            integration = integration.add(orderItem.getIntegrationAmount());
            promotion = promotion.add(orderItem.getPromotionAmount());
            giftGrowth = giftGrowth.add(new BigDecimal(orderItem.getGiftGrowth()));
            giftIntegration = giftIntegration.add(new BigDecimal(orderItem.getGiftIntegration()));

            orderTotalPrice = orderTotalPrice.add(orderItem.getRealAmount());
        }
        // orderItem.getRealAmount()已经是当前商品优惠后的价格，这里不用再减优惠
        // 减去优惠券、会员积分、商品优惠后的价格 ？
//        orderTotalPrice = orderTotalPrice.subtract(coupon).subtract(integration).subtract(promotion); ？

        orderEntity.setTotalAmount(orderTotalPrice);
        orderEntity.setPayAmount(orderTotalPrice.add(orderEntity.getFreightAmount()));
        orderEntity.setCouponAmount(coupon);
        orderEntity.setIntegrationAmount(integration);
        orderEntity.setPromotionAmount(promotion);

        // 购物获得积分、成长值
        orderEntity.setIntegration(giftIntegration.intValue());
        orderEntity.setGrowth(giftGrowth.intValue());

        orderEntity.setDeleteStatus(0);
        orderEntity.setAutoConfirmDay(7);
    }

    /**
     * 构建订单
     * @return
     */
    private OrderEntity buildOrder() {
        OrderEntity orderEntity = new OrderEntity();
        MemberDto member = GulimallAuthInterceptor.memberThreadLocal.get();
        orderEntity.setCreateTime(new Date());
        String orderSn = IdWorker.getTimeId();
        orderEntity.setOrderSn(orderSn);

        // 代付款状态
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());

        orderEntity.setMemberId(member.getId());
        orderEntity.setMemberUsername(member.getUsername());
        orderEntity.setPayType(1);

        // 地址设置
        List<OrderAddressVo> memberAddress = memberAddressService.getMemberAddress(member.getId());
        List<OrderAddressVo> addr = memberAddress.stream().filter(o -> o.getDefaultStatus() == 1).collect(Collectors.toList());
        OrderAddressVo orderAddressVo = addr.get(0);
        orderEntity.setReceiverName(orderAddressVo.getName());
        orderEntity.setReceiverPhone(orderAddressVo.getPhone());
        orderEntity.setReceiverPostCode(orderAddressVo.getPostCode());
        orderEntity.setReceiverProvince(orderAddressVo.getProvince());
        orderEntity.setReceiverCity(orderAddressVo.getCity());
        orderEntity.setReceiverRegion(orderAddressVo.getRegion());
        orderEntity.setReceiverDetailAddress(orderAddressVo.getDetailAddress());

        return orderEntity;
    }

    /**
     * 构建订单项--一个订单中包含多个商品，每个商品一个OrderItemEntity对象
     * @param item 购物车单个商品
     * @return
     */
    private OrderItemEntity buildOrderItem(CartItem item, String orderSn) {
        OrderItemEntity orderItemEntity = new OrderItemEntity();

        orderItemEntity.setOrderSn(orderSn);

        CompletableFuture<Void> setSkuInfo = CompletableFuture.runAsync(() -> {
            SkuInfo skuInfoBySkuId = productFeignService.getSkuInfoBySkuId(item.getSkuId());
            // 设置sku信息
            orderItemEntity.setSkuId(item.getSkuId());
            orderItemEntity.setSkuName(skuInfoBySkuId.getSkuName());
            orderItemEntity.setSkuPic(skuInfoBySkuId.getSkuDefaultImg());
            orderItemEntity.setSkuPrice(skuInfoBySkuId.getPrice());
            orderItemEntity.setSkuQuantity(item.getCount());
            orderItemEntity.setSkuAttrsVals(StringUtils.arrayToDelimitedString(item.getSkuAttr().toArray(), ";"));
        }, executor);

        CompletableFuture<Void> setSpuInfo = CompletableFuture.runAsync(() -> {
            // 设置spu信息
            SpuInfo spuInfoBySkuId = productFeignService.getSpuInfoBySkuId(item.getSkuId());
            orderItemEntity.setSpuId(spuInfoBySkuId.getId());
            orderItemEntity.setSpuName(spuInfoBySkuId.getSpuName());
            orderItemEntity.setSpuBrand(spuInfoBySkuId.getBrandId().toString());
            orderItemEntity.setCategoryId(spuInfoBySkuId.getCatalogId());
        }, executor);


        try {
            CompletableFuture.allOf(setSkuInfo, setSpuInfo).get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        // 设置积分信息
        orderItemEntity.setGiftGrowth(item.getPrice().multiply(new BigDecimal(item.getCount())).intValue());
        orderItemEntity.setGiftIntegration(item.getPrice().multiply(new BigDecimal(item.getCount())).intValue());

        // set price
        orderItemEntity.setPromotionAmount(new BigDecimal(0));
        orderItemEntity.setIntegrationAmount(new BigDecimal(0));
        orderItemEntity.setCouponAmount(new BigDecimal(0));
        BigDecimal originPrices = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity()));

        BigDecimal realAmount = originPrices.subtract(orderItemEntity.getPromotionAmount()).subtract(orderItemEntity.getIntegrationAmount()).subtract(orderItemEntity.getCouponAmount());
        orderItemEntity.setRealAmount(realAmount);

        return orderItemEntity;
    }

    /**
     * 订单列表
     * @return  {@linkplain OrderListVo}.
     */
    @Override
    public List<OrderListVo> getOrderList() {
        MemberDto memberDto = GulimallAuthInterceptor.memberThreadLocal.get();
        Long id = memberDto.getId();

        // 当前用户的所有订单
        List<OrderEntity> orders = this.list(new QueryWrapper<OrderEntity>().eq("member_id", id));

        // 根据订单号查出每个订单详细商品
        List<String> orderSns = orders.stream().map(OrderEntity::getOrderSn).collect(Collectors.toList());
        List<OrderItemEntity> orderItems = orderItemService.list(new QueryWrapper<OrderItemEntity>().in("order_sn", orderSns));

        // 组合并返回
        return orders.stream().map(odr -> {
            OrderListVo orderListVo = new OrderListVo();
            orderListVo.setOrder(odr);
            // 过滤出当前订单号的商品
            List<OrderItemEntity> collect = orderItems.stream().filter(item -> Objects.equals(item.getOrderSn(), odr.getOrderSn())).collect(Collectors.toList());
            orderListVo.setOrderItem(collect);
            return orderListVo;
        }).collect(Collectors.toList());
    }

    /**
     * 处理OrderEntity类消息方法
     * @param message 消息
     * @param connect 订单
     * @param channel 信道
     */
//    @RabbitHandler
    public void messageQueueTest(Message message, OrderEntity connect, Channel channel) {
        MessageProperties messageProperties = message.getMessageProperties();
        long deliveryTag = messageProperties.getDeliveryTag();
        String type = messageProperties.getType();
        log.info("对象类包名:{},处理OrderEntity类消息方法:{}, 签收标签:{}", type, connect, deliveryTag);

        // 确认收货，消费msg
//        channel.basicAck();

        // 不收货
        // deliveryTag:收货id，此id为自增值，从1开始，每个消息一个
        // multiple: 多
        // requeue: 重新入队。false不队，true重新入队。
        try {
            channel.basicNack(deliveryTag,false, false);
            log.info("丢弃了{}", deliveryTag);
            System.out.println();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理日期类消息方法
     * @param message 消息
     * @param date 日期
     * @param channel 信道
     */
//    @RabbitHandler
    public void messageByDate(Message message, Date date, Channel channel) {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        log.info("处理日期类消息方法:{}, 签收标签:{}", date, deliveryTag);
    }
}